/*
  wxTriangulator
  Copyright 2008 Boleslaw Kulbabinski

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "globals.hpp"
#include "main.hpp"
#include "window.hpp"
#include "icon.xpm"

/*
  window constructor
*/
TriWindow::TriWindow(const wxString& title, const wxPoint& pos, const wxSize& size)
:wxFrame(NULL, wxID_ANY, title, pos, size)
{
	SetBackgroundColour(wxColour(255,255,255));
	SetIcon(wxIcon(tri_icon_xpm));
	CreateStatusBar();

	// creating menu
	wxMenu *menuFile = new wxMenu;
	menuFile->Append(1, _T("&Triangulate") );
	menuFile->Append(2, _T("&Clear") );
	menuFile->AppendSeparator();
	menuFile->Append(3, _T("E&xit") );

	wxMenuBar *menuBar = new wxMenuBar;
	menuBar->Append( menuFile, _T("&Actions") );

	SetMenuBar( menuBar );

	// connecting events with handler functions
	Connect(1, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TriWindow::OnTriangulate));
	Connect(2, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TriWindow::OnClear));
	Connect(3, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(TriWindow::OnQuit));

	Connect(wxEVT_PAINT, wxPaintEventHandler(TriWindow::OnPaint));
	Connect(wxEVT_MOTION, wxMouseEventHandler(TriWindow::OnMouseMove));
	Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(TriWindow::OnMouseLeave));
	Connect(wxEVT_LEFT_UP, wxMouseEventHandler(TriWindow::OnLeftClick));
}

void TriWindow::OnClear(wxCommandEvent& event)
{
	wxGetApp().Clear();
	wxClientDC dc(this);
	dc.Clear();
}

void TriWindow::OnQuit(wxCommandEvent& event)
{
	Destroy();
}

void TriWindow::OnTriangulate(wxCommandEvent& event)
{
	if(!wxGetApp().IsTriangulated())
	{
		SetStatusText(wxT("Running algorithm ..."));
		try
		{
			wxGetApp().Triangulate();

			wxClientDC dc(this);
			draw_triangulation(&dc);
			SetStatusText(wxT("Done."));
		}
		catch(string error)
		{
			SetStatusText(wxString(error.c_str(), wxConvUTF8));
		}
	}
	else
	{
		SetStatusText(wxT("The polygon is already triangulated."));
	}
}

void TriWindow::OnPaint(wxPaintEvent& event)
{
	wxPaintDC dc(this);

	if(wxGetApp().IsFinished())
	{
		draw_polygon(&dc, true);
		if(wxGetApp().IsTriangulated())
		{
			draw_triangulation(&dc);
		}
	}
	else
	{
		draw_polygon(&dc, false);
	}
}

void TriWindow::OnMouseMove(wxMouseEvent& event)
{
	wxString str;
	str.Printf(wxT("x: %ld y: %ld"), event.m_x, event.m_y);
	SetStatusText(str);
}

void TriWindow::OnMouseLeave(wxMouseEvent& event)
{
	SetStatusText(wxT(""));
}

/*
  creates a new vertex or finish drawing the polygon
*/
void TriWindow::OnLeftClick(wxMouseEvent& event)
{
	if(!wxGetApp().IsFinished())
	{
		wxClientDC dc(this);
		Point other_end;
		VP_c_iter begin = wxGetApp().GetPolygon().first;

		if(!wxGetApp().IsEmpty() && test_vicinity(*begin, Point(event.m_x, event.m_y)))
		{
			// the first vertex was selected so we end the polygon
			try
			{
				other_end = wxGetApp().Finish();
				SetStatusText(wxT("Polygon Finished!"));
				dc.DrawLine(other_end.x, other_end.y, begin->x, begin->y);
			}
			catch(string error)
			{
				SetStatusText(wxString(error.c_str(), wxConvUTF8));
			}
		}
		else
		{
			other_end = wxGetApp().AddPoint(event.m_x, event.m_y);
			SetStatusText(wxT("Vertex Added!"));

			draw_vertex(&dc, event.m_x, event.m_y);
			dc.DrawLine(other_end.x, other_end.y, event.m_x, event.m_y);
		}
	}
	else
	{
		SetStatusText(wxT("The polygon is finished, clear it to begin a new one."));
	}
}

/*
  tests if two given points are close enough to treat them as identical (in order to finish the polygon)
*/
bool TriWindow::test_vicinity(Point A, Point B)
{
	if(abs(A.x - B.x) < 3 && abs(A.y - B.y) < 3)
		return true;
	else
		return false;
}

void TriWindow::draw_vertex(wxDC *dc, long x, long y)
{
	dc->SetPen(*wxBLACK_PEN);
	dc->DrawCircle(x,y,2);
}

void TriWindow::draw_polygon(wxDC *dc, bool closed)
{
	pair<VP_c_iter, VP_c_iter> polygon = wxGetApp().GetPolygon();

	VP_c_iter polygon_begin = polygon.first;
	VP_c_iter polygon_end = polygon.second;

	long prev_x, prev_y;

	if(closed)
	{
		// draw the edge between the first and the last vertex
		dc->DrawLine(polygon_begin->x, polygon_begin->y, (polygon_end - 1)->x, (polygon_end - 1)->y);
	}

	for(VP_c_iter iter = polygon_begin; iter != polygon_end; iter++)
	{
		draw_vertex(dc, iter->x, iter->y);
		if(iter != polygon_begin)
		{
			dc->DrawLine(prev_x, prev_y, iter->x, iter->y);
		}
		prev_x = iter->x;
		prev_y = iter->y;
	}
}

void TriWindow::draw_triangulation(wxDC *dc)
{
	pair<VL_c_iter, VL_c_iter> segments = wxGetApp().GetTriangulation();

	VL_c_iter seg_begin = segments.first;
	VL_c_iter seg_end = segments.second;

	dc->SetPen(*wxRED_PEN);
	for(VL_c_iter iter = seg_begin; iter != seg_end; iter++)
	{
		dc->DrawLine(iter->A.x, iter->A.y, iter->B.x, iter->B.y);
	}
}

